﻿using FileHelpers;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ZillowApiClient.Data;
using ZillowApiClient.Data.Model;

namespace ZillowApiClient
{
    internal class BatchZillowProcess
    {
        private AppConfig Config = new AppConfig();
        public List<InputRecord> Requests { get; set; }

        public List<result> Results { get; set; }

        internal BatchZillowProcess()
        {
            Requests = new List<InputRecord>();
            Results = new List<result>();
        }

        public async void Run()
        {
            try
            {
                var sw = new Stopwatch();

                Console.WriteLine($"Reading files from {Config.InputPath} ");
                sw.Start();
                ReadFile();
                sw.Stop();
                Console.WriteLine($"Finished reading {Requests.Count} records in {sw.ElapsedMilliseconds} ms.");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Could not read file because: {ex.Message}");
            }

            if (Requests == null) return;
            try
            {
                var sw = new Stopwatch();
                for (var i = 0; i < Requests.Count; i++)
                {
                    Console.WriteLine($"Retrieving {Requests[i].address} from {Requests[i].city},{Requests[i].state}");
                    sw.Start();
                    await GetResults(Requests[i]);
                    sw.Stop();
                    if (Requests[i].results != null)
                    {
                        Console.WriteLine($"...Found {Requests[i].results.Count} records in {sw.ElapsedMilliseconds} ms.");
                    }
                    else
                    {
                        Console.WriteLine($"...Finished in {sw.ElapsedMilliseconds} ms.");
                    }
                    sw.Reset();
                    var minDivisor = Math.Max(Config.PauseInterval, 1);
                    if (i != 0 && i % minDivisor == 0)
                    {
                        Console.WriteLine("Pausing for 10s to prevent API rejection...");
                        Thread.Sleep(5000);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Could not get results because: {ex.Message}");
            }

            if (Results == null) return;
            try
            {
                Console.Write("Writing file...");
                var sw = new Stopwatch();
                sw.Start();
                WriteFile();
                sw.Stop();
                Console.WriteLine($"Finished in {sw.ElapsedMilliseconds} ms.");
                Console.WriteLine($"Process complete. You can find your output file in {Config.OutputPath}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Could not write file because: {ex.Message}");
            }
        }

        private void ReadFile()
        {
            var engine = new FileHelperAsyncEngine<InputRecord>();
            engine.Options.IgnoreFirstLines = Config.InputHeaderLines;
            using (engine.BeginReadFile(Config.InputPath))
            {
                foreach (var request in engine)
                {
                    if (request != null)
                        Requests.Add(request);
                }
            }
        }

        private void WriteFile()
        {
            var engine = new FileHelperEngine<OutputRecord>();
            if (Config.IncludeOutputHeader)
                engine.HeaderText = engine.GetFileHeader();
            try
            {
                engine.WriteFile(Config.OutputPath, Requests.Select(x => x.ToOutput()));
            }
            catch
            {
                engine.WriteFile(Config.OutputPath.Replace(".csv", string.Empty) + DateTime.Now.Millisecond + ".csv", Requests.Select(x => x.ToOutput()));
            }
        }

        private async Task GetResults(InputRecord input)
        {
            var client = new ZillowClient(Config.ZillowApiId, Config.HttpTimeout);
            List<result> results = null;
            try
            {
                results = await client.GetAsync(input.address.Trim(), input.city.Trim() + ", " + input.state.Trim());
                input.results = results;
            }
            catch (HttpRequestException ex)
            {
                input.Exceptions = ex.Message;
            }
            catch (AggregateException ex)
            {
                var exList = ex.Flatten();
                foreach (var innerEx in exList.InnerExceptions)
                {
                    input.Exceptions = (input.Exceptions ?? string.Empty) + innerEx.Message;
                }
            }
            catch (Exception ex)
            {
                input.Exceptions = (input.Exceptions ?? string.Empty) + ex.Message;
            }
        }
    }
}